<template>
  <span class="animated-number">{{ displayValue }}</span>
</template>

<script setup>
import { ref, watch, onMounted } from 'vue'

const props = defineProps({
  value: {
    type: Number,
    required: true
  },
  duration: {
    type: Number,
    default: 1000
  },
  easing: {
    type: String,
    default: 'easeOutCubic',
    validator: (value) => ['linear', 'easeInCubic', 'easeOutCubic', 'easeInOutCubic'].includes(value)
  },
  format: {
    type: Function,
    default: (value) => Math.round(value).toLocaleString()
  },
  autoStart: {
    type: Boolean,
    default: true
  }
})

const displayValue = ref(0)
const animationId = ref(null)

// 缓动函数
const easingFunctions = {
  linear: (t) => t,
  easeInCubic: (t) => t * t * t,
  easeOutCubic: (t) => 1 - Math.pow(1 - t, 3),
  easeInOutCubic: (t) => t < 0.5 ? 4 * t * t * t : 1 - Math.pow(-2 * t + 2, 3) / 2
}

const animate = (from, to, duration = props.duration) => {
  // 清除之前的动画
  if (animationId.value) {
    cancelAnimationFrame(animationId.value)
  }

  const startTime = performance.now()
  const difference = to - from
  const easingFunction = easingFunctions[props.easing]

  const step = (currentTime) => {
    const elapsed = currentTime - startTime
    const progress = Math.min(elapsed / duration, 1)
    
    const easedProgress = easingFunction(progress)
    const currentValue = from + (difference * easedProgress)
    
    displayValue.value = props.format(currentValue)
    
    if (progress < 1) {
      animationId.value = requestAnimationFrame(step)
    } else {
      displayValue.value = props.format(to)
      animationId.value = null
    }
  }

  animationId.value = requestAnimationFrame(step)
}

const startAnimation = () => {
  const currentDisplayValue = typeof displayValue.value === 'string' 
    ? parseFloat(displayValue.value.replace(/,/g, '')) || 0
    : displayValue.value
  
  animate(currentDisplayValue, props.value)
}

// 监听值变化
watch(() => props.value, (newValue, oldValue) => {
  if (newValue !== oldValue) {
    startAnimation()
  }
})

// 组件挂载时自动开始动画
onMounted(() => {
  if (props.autoStart) {
    startAnimation()
  } else {
    displayValue.value = props.format(props.value)
  }
})

// 暴露方法给父组件
defineExpose({
  startAnimation,
  reset: () => {
    displayValue.value = props.format(0)
  }
})
</script>

<style scoped>
.animated-number {
  display: inline-block;
  font-variant-numeric: tabular-nums;
  transition: color 0.2s ease;
}
</style>